package sf.database.mapper.handle;

import sf.database.support.DMLType;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;

public class HandleHelp {

    /**
     * 根据sql语句判断sql类型，用于对应到DBClient操作
     * @param sql
     * @return
     */
    public static DMLType getTypeBySql(String sql) {

        String sqlType = getFirstToken(sql);

        switch (sqlType) {
            case "select":
                return DMLType.SELECT;
            case "insert":
                return DMLType.INSERT;
            case "delete":
                return DMLType.DELETE;
            case "update":
                return DMLType.UPDATE;
            case "create":
                return DMLType.OTHER;
            case "drop":
                return DMLType.OTHER;
            case "truncate":
                return DMLType.OTHER;
            default:
                return DMLType.OTHER; //unknow

        }
    }

    public static Class getPageType(Type type, Class defaultClass) {
        if (type instanceof Class) {
            return defaultClass;
        } else {
            Type t = ((ParameterizedType) type)
                    .getActualTypeArguments()[0];
            if (t instanceof ParameterizedType) {
                return getParamterTypeClass(t);
            } else if (t instanceof WildcardType || t instanceof TypeVariable) {
                //丢失类型，只能用返回类型来判断
                return defaultClass;
            } else {
                return (Class) t;
            }
        }
    }

    public static Class getType(Type type) {
        if (type instanceof Class) {
            return (Class) type;
        } else if (type instanceof ParameterizedType) {
            Type t = ((ParameterizedType) type)
                    .getActualTypeArguments()[0];
            if (t instanceof ParameterizedType) {
                return getParamterTypeClass(t);
            } else {
                return (Class) t;
            }
        } else {
            return null;
        }
    }

    public static Class getParamterTypeClass(Type t) {
        if (t instanceof Class) {
            return (Class) t;
        } else {
            return (Class) ((ParameterizedType) t).getRawType();
        }

    }

    public static String getFirstToken(String sql) {
        boolean start = false;
        int startIndex = 0;
        for (int i = 0; i < sql.length(); i++) {
            char c = sql.charAt(i);
            if (!start) {
                if (!isSpecialChar(c)) {
                    start = true;
                    startIndex = i;
                }
                continue;
            }

            if (isSpecialChar(c)) {
                return sql.substring(startIndex, i).toLowerCase();
            }

        }
        return "";
    }

    public static boolean isSpecialChar(char c) {
        return c == ' ' || c == '\t' || c == '\r' || c == '\n';
    }

    public static Class getRetType(Method method, Class entityClass) {
        Type type = method.getGenericReturnType();
        if (type instanceof ParameterizedType) {
            return (Class) ((ParameterizedType) method.getGenericReturnType())
                    .getActualTypeArguments()[0];
        } else {
            return entityClass;
        }
    }

    static class CallKey {
        Method m;
        Class entityClass;

        public CallKey(Method m, Class entityClass) {
            this.m = m;
            this.entityClass = entityClass;
        }

        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + ((entityClass == null) ? 0 : entityClass.hashCode());
            result = prime * result + ((m == null) ? 0 : m.hashCode());
            return result;
        }

        @Override
        public boolean equals(Object obj) {

            CallKey other = (CallKey) obj;
            return other.entityClass == this.entityClass && this.m.equals(other.m);
        }

    }
}
